package com;

import com.pojo.GameMap;
import com.pojo.Player;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.pojo.Player.resourcesType.*;

public class Game1 {

    // 模型参数调整
    private static Integer maxCapital = 0; //最优抉择收益值
    private static Integer initWaterNum = 180;    //初始携带水
    private static Integer initFoodNum = 330;      //初始携带食物
    private static final Integer initCapital = 10000; //起始资金
    private static final Integer waterWeight = 3; //单份水重量
    private static final Integer foodWeight = 2; // 单份食物重量
    private static final Integer weightMax = 1200; // 负重上限
    private static final Integer average = weightMax/(waterWeight+foodWeight); // 补给平衡值
    private static final Integer profit = 1000; //挖矿收益
    private static final Integer terminationDate = 30; // 终止日期
    private static final Integer waterPrice = 5; // 单份水价格
    private static final Integer foodPrice = 10; // 单份食物价格

    private static final Integer sunnyWater = 5; // 晴天基础水消耗
    private static final Integer sunnyFood = 7; // 晴天基础食物消耗
    private static final Integer highWater = 8; // 高温基础水消耗
    private static final Integer highFood = 6;  // 高温基础食物消耗
    private static final Integer sandstormWater = 10; // 沙暴基础水消耗
    private static final Integer sandstormFood = 10;  // 沙暴基础食物消耗

    private static final Integer threshold = 40; // 食物补给阈值
    private static final Integer balance = 0; // 食物-水 补给平衡参数

    private static List<Player.resourcesType> resourcesList = Arrays.asList( // 天气分布
            high, high, sunny, Sandstorm, sunny, high,Sandstorm,sunny,high,high,
            Sandstorm,high,sunny,high,high,high,Sandstorm,Sandstorm,high,high,
            sunny,sunny,high,sunny,Sandstorm,high,sunny,sunny,high,high
    );

    /**
     * 天气-水基础消耗 映射表
     */
    private static HashMap<Player.resourcesType, Integer> waterConsume = new HashMap<Player.resourcesType, Integer>() {{
        put(sunny, sunnyWater);
        put(high, highWater);
        put(Sandstorm, sandstormWater);
    }};
    /**
     * 天气-食物基础消耗 映射表
     */
    private static HashMap<Player.resourcesType, Integer> foodConsume = new HashMap<Player.resourcesType, Integer>() {{
        put(sunny, sunnyFood);
        put(high, highFood);
        put(Sandstorm, sandstormFood);
    }};

    /**
     * 启动入口
     */
    public static void main(String[] args) {
        GameMap gameMap = initMap();
        Player player = new Player(initWaterNum, initFoodNum,initCapital-initWaterNum*waterPrice-initFoodNum*foodPrice, 0, gameMap,0,0);
        play(player, gameMap, new HashMap<Integer, Player>());
    }

    /**
     * 动态规划求解最优策略问题。
     * @param player 游戏玩家
     * @param gameMap 当前地图节点
     * @param path 行动路线
     */
    private static void play(Player player, GameMap gameMap, Map<Integer, Player> path){
        if ((player.getWaterNum()<0||player.getFoodNum()<0)){
            return;
        }
        if (gameMap.getMapType()==GameMap.type.village){
            supply(threshold,player,balance);
        }
        path.put(player.getNowDate(),new Player(player.getWaterNum(),player.getFoodNum(),player.getCapital(),gameMap));
        if (gameMap.getMapType() == GameMap.type.end){
            Integer capital = player.getCapital();
            if (initFoodNum > player.getFoodEat()){
                capital += player.getFoodNum() * foodPrice;
            }else{
                capital += player.getFoodNum() * foodPrice * 2;
            }
            if (initWaterNum > player.getWaterEat()){
                capital += player.getWaterNum() * waterPrice;
            }else{
                capital += player.getWaterNum() * waterPrice * 2;
            }

            if (capital > maxCapital){
                System.out.println("---------------------------------------------------");
                System.out.printf("成功通关!\n%-13s\t%-13s\t%-13s\n","旅途总收益="+capital,"旅途总水消耗="+player.getWaterEat(),"旅途总食物消耗="+player.getFoodEat());
                maxCapital = capital;
                Integer waterNum = player.getWaterNum();
                Integer foodNum = player.getFoodNum();
                HashMap<Integer,Player> printPath = new HashMap<>(path);
                for (int i = printPath.size()-1;i>=0;i--){
                    Player player1 = printPath.get(i);
                    printPath.put(i,new Player(player1.getWaterNum()-waterNum,player1.getFoodNum()-foodNum,capital,player1.getMapId()));
                    if (player1.getMapId().getMapType()== GameMap.type.village||player1.getMapId().getMapType()== GameMap.type.start){
                        break;
                    }
                }
                System.out.println("具体行程安排如下:");
                for (Integer day : printPath.keySet()){
                    System.out.print("第"+(day)+"天:");
                    printPath.get(day).printPlayer();
                }
                System.out.println("---------------------------------------------------");
            }
            return;
        }
        if (player.getNowDate().equals(terminationDate)){
            return;
        }
        Player.resourcesType resources = resourcesList.get(player.getNowDate());
        Integer waterBasics = waterConsume.get(resources);
        Integer foodBasics = foodConsume.get(resources);
        Integer waterNum = player.getWaterNum();
        Integer foodNum = player.getFoodNum();
        Integer capital = player.getCapital();
        Integer nowDate = player.getNowDate();
        if (resources!= Sandstorm){
            for (GameMap map : gameMap.getAdjacent()){
                play(new Player(waterNum-(waterBasics*2),foodNum-(foodBasics*2),capital,nowDate+1,map,player.getWaterEat()+waterBasics*2,player.getFoodEat()+foodBasics*2),map,new HashMap<>(path));
            }
            if (gameMap.getMapType()==GameMap.type.mine){
                play(new Player(waterNum-(waterBasics*3),foodNum-(foodBasics*3),capital+profit,nowDate+1,gameMap,player.getWaterEat()+waterBasics*3,player.getFoodEat()+foodBasics*3),gameMap,new HashMap<>(path));
                play(new Player(waterNum-(waterBasics),foodNum-(foodBasics),capital,nowDate+1,gameMap,player.getWaterEat()+waterBasics,player.getFoodEat()+foodBasics),gameMap,new HashMap<>(path));
            }
        }else{
            if (gameMap.getMapType()==GameMap.type.mine){
                play(new Player(waterNum-(waterBasics*3),foodNum-(foodBasics*3),capital+profit,nowDate+1,gameMap,player.getWaterEat()+waterBasics*3,player.getFoodEat()+foodBasics*3),gameMap,new HashMap<>(path));
            }
            play(new Player(waterNum-(waterBasics),foodNum-(foodBasics),capital,nowDate+1,gameMap,player.getWaterEat()+waterBasics,player.getFoodEat()+foodBasics),gameMap,new HashMap<>(path));
        }
    }

    /**
     * 游戏地图逻辑绘制
     * @return 游戏地图起始节点
     */
    public static GameMap initMap(){
        GameMap m1 = new GameMap(1,null, GameMap.type.start);
        GameMap m9 = new GameMap(9,null, GameMap.type.land);
        GameMap m12 = new GameMap(12,null, GameMap.type.mine);
        GameMap m14 = new GameMap(14,null, GameMap.type.land);
        GameMap m15 = new GameMap(15,null, GameMap.type.village);
        GameMap m21 = new GameMap(21,null, GameMap.type.land);
        GameMap m22 = new GameMap(22,null, GameMap.type.land);
        GameMap m23 = new GameMap(23,null, GameMap.type.land);
        GameMap m24 = new GameMap(24,null, GameMap.type.land);
        GameMap m25 = new GameMap(25,null, GameMap.type.land);
        GameMap m26 = new GameMap(26,null, GameMap.type.land);
        GameMap m27 = new GameMap(27,null, GameMap.type.end);
        m1.setAdjacent(Arrays.asList(m25));
        m25.setAdjacent(Arrays.asList(m24));
        m24.setAdjacent(Arrays.asList(m23));
        m23.setAdjacent(Arrays.asList(m22));
        m22.setAdjacent(Arrays.asList(m9));
        m21.setAdjacent(Arrays.asList(m27));
        m9.setAdjacent(Arrays.asList(m15,m21));
        m15.setAdjacent(Arrays.asList(m9,m14));
        m14.setAdjacent(Arrays.asList(m12,m15));
        m12.setAdjacent(Arrays.asList(m14));
        m26.setAdjacent(Arrays.asList(m27));
        m27.setAdjacent(Arrays.asList());
        return m1;
    }

    /**
     * 村庄补给策略
     * @param threshold 食物补给阈值
     * @param player 补给玩家
     * @param balance 补给平衡参数
     */
    public static void supply(int threshold,Player player,int balance){
        if (player.getFoodNum() > threshold){
            Integer waterNum = (weightMax - (player.getFoodNum() * foodWeight + player.getWaterNum() * waterWeight)) / waterWeight;
            player.setWaterNum(player.getWaterNum() + waterNum);;
            player.setCapital(player.getCapital() - waterNum*waterPrice*2);
        }else{
            Integer waterD = (average-(balance*waterWeight/foodWeight))-player.getWaterNum();
            Integer foodD = (average+balance)-player.getFoodNum();
            if (waterD > 0){
                player.setWaterNum(player.getWaterNum()+waterD);
                player.setCapital(player.getCapital()-waterD*waterPrice*2);
            }
            if (foodD > 0){
                player.setFoodNum(player.getFoodNum()+foodD);
                player.setCapital(player.getCapital() - foodD*foodPrice*2);
            }
        }
    }
}
